﻿using System;
using System.Text;
using System.Reflection;
using System.Security.Cryptography;

namespace SSPharma.Common
{
    /// <summary>
    /// Enables encryption of strings using the MD5 algorithm.
    /// </summary>
    public class MD5Crypt
    {
        /// <summary>
        /// Encrypts a string using a specified security key with
        /// the option to hash.
        /// </summary>
        /// <param name="toEncrypt">String to encrypt</param>
        /// <param name="securityKey">The key to apply to the encryption</param>
        /// <param name="useHashing">Weather hashing is used</param>
        /// <returns>The encrpyted string</returns>
        public static string Encrypt(string toEncrypt, string securityKey, bool useHashing)
        {

            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                // Validate inputs
                ValidateInput(toEncrypt);
                ValidateInput(securityKey);

                // If hashing use get hashcode regards to your key
                if (useHashing)
                {
                    MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                    keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                    // Always release the resources and flush data
                    // of the Cryptographic service provide. Best Practice
                    hashmd5.Clear();
                }

                else
                {
                    keyArray = UTF8Encoding.UTF8.GetBytes(securityKey);
                }

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB (Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode (if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateEncryptor();

                // Transform the specified region of bytes array to resultArray
                byte[] resultArray =
                  cTransform.TransformFinalBlock(toEncryptArray, 0,
                  toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();

                // Return the encrypted data into unreadable string format
                retVal = Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.EncryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Decrypts a specified key against the original security
        /// key, with the option to hash.
        /// </summary>
        /// <param name="cipherString">String to decrypt</param>
        /// <param name="securityKey">The original security key</param>
        /// <param name="useHashing">Weather hashing is enabled</param>
        /// <returns>The decrypted key</returns>
        public static string Decrypt(string cipherString, string securityKey, bool useHashing)
        {

            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = Convert.FromBase64String(cipherString);

                // Validate inputs
                ValidateInput(cipherString);
                ValidateInput(securityKey);

                if (useHashing)
                {
                    // If hashing was used get the hash code with regards to your key
                    MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                    keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                    // Release any resource held by the MD5CryptoServiceProvider
                    hashmd5.Clear();
                }
                else
                {
                    // If hashing was not implemented get the byte code of the key
                    keyArray = UTF8Encoding.UTF8.GetBytes(securityKey);
                }

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB(Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode(if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(
                                     toEncryptArray, 0, toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();

                // Return the Clear decrypted TEXT
                retVal = UTF8Encoding.UTF8.GetString(resultArray);

            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.DecryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Encrypts a string using a default security key to hash.
        /// </summary>
        /// <param name="toEncrypt">String to encrypt</param>
        /// <returns>The encrpyted string</returns>
        public static string Encrypt(string toEncrypt)
        {
            string securityKey = "TAM NN";
            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                // Validate inputs
                ValidateInput(toEncrypt);
                ValidateInput(securityKey);
                
                // Get hashcode regards to your key
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                // Always release the resources and flush data
                // of the Cryptographic service provide. Best Practice
                hashmd5.Clear();

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB (Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode (if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateEncryptor();

                // Transform the specified region of bytes array to resultArray
                byte[] resultArray =
                  cTransform.TransformFinalBlock(toEncryptArray, 0,
                  toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();

                // Return the encrypted data into unreadable string format
                retVal = Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.EncryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Decrypts a default key against the original security key to hash.
        /// </summary>
        /// <param name="cipherString">String to decrypt</param>
        /// <returns>The decrypted key</returns>
        public static string Decrypt(string cipherString)
        {

            string securityKey = "TAM NN";
            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = Convert.FromBase64String(cipherString);

                // Validate inputs
                ValidateInput(cipherString);
                ValidateInput(securityKey);

                // Get the hash code with regards to your key
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                // Release any resource held by the MD5CryptoServiceProvider
                hashmd5.Clear();

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB(Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode(if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateDecryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(
                                     toEncryptArray, 0, toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();

                // Return the Clear decrypted TEXT
                retVal = UTF8Encoding.UTF8.GetString(resultArray);

            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.DecryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Encrypts a string using a specified security key with
        /// the option to hash.
        /// </summary>
        /// <param name="toEncrypt">String to encrypt</param>
        /// <param name="securityKey">The key to apply to the encryption</param>
        /// <param name="useHashing">Weather hashing is used</param>
        /// <returns>The encrpyted string</returns>
        public static string EncryptPassword(string toEncrypt, string securityKey, bool useHashing)
        {

            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                // Validate inputs
                ValidateInput(toEncrypt);
                ValidateInput(securityKey);

                // If hashing use get hashcode regards to your key
                if (useHashing)
                {
                    MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                    keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                    // Always release the resources and flush data
                    // of the Cryptographic service provide. Best Practice
                    hashmd5.Clear();
                }

                else
                {
                    keyArray = UTF8Encoding.UTF8.GetBytes(securityKey);
                }

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB (Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode (if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateEncryptor();

                // Transform the specified region of bytes array to resultArray
                byte[] resultArray =
                  cTransform.TransformFinalBlock(toEncryptArray, 0,
                  toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();


                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < resultArray.Length; i++)
                {
                    sb.Append(resultArray[i].ToString("X2"));
                }

                // Return the encrypted data into string format
                retVal = sb.ToString();

            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.EncryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Encrypts a string using a default security key to hash.
        /// </summary>
        /// <param name="toEncrypt">String to encrypt</param>
        /// <returns>The encrpyted string</returns>
        public static string EncryptPassword(string toEncrypt)
        {
            string securityKey = "TAM NN";
            string retVal = string.Empty;

            try
            {
                byte[] keyArray;
                byte[] toEncryptArray = UTF8Encoding.UTF8.GetBytes(toEncrypt);

                // Validate inputs
                ValidateInput(toEncrypt);
                ValidateInput(securityKey);

                // Get hashcode regards to your key
                MD5CryptoServiceProvider hashmd5 = new MD5CryptoServiceProvider();
                keyArray = hashmd5.ComputeHash(UTF8Encoding.UTF8.GetBytes(securityKey));

                // Always release the resources and flush data
                // of the Cryptographic service provide. Best Practice
                hashmd5.Clear();

                TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();

                // Set the secret key for the tripleDES algorithm
                tdes.Key = keyArray;

                // Mode of operation. there are other 4 modes.
                // We choose ECB (Electronic code Book)
                tdes.Mode = CipherMode.ECB;

                // Padding mode (if any extra byte added)
                tdes.Padding = PaddingMode.PKCS7;

                ICryptoTransform cTransform = tdes.CreateEncryptor();

                // Transform the specified region of bytes array to resultArray
                byte[] resultArray =
                  cTransform.TransformFinalBlock(toEncryptArray, 0,
                  toEncryptArray.Length);

                // Release resources held by TripleDes Encryptor
                tdes.Clear();


                StringBuilder sb = new StringBuilder();

                for (int i = 0; i < resultArray.Length; i++)
                {
                    sb.Append(resultArray[i].ToString("X2"));
                }

                // Return the encrypted data into string format
                retVal = sb.ToString();

            }

            catch (Exception ex)
            {
                throw new EncryptionException(EncryptionException.Code.EncryptionFailure, ex, MethodBase.GetCurrentMethod());
            }

            return retVal;
        }

        /// <summary>
        /// Encode Password
        /// </summary>
        /// <param name="originalPassword">
        /// The original password.
        /// </param>
        /// <returns>
        /// string value.
        /// </returns>
        /// <exception cref="System.ArgumentNullException">Thrown when </exception>
        /// <exception cref="System.Text.EncoderFallbackException">Thrown when </exception>
        /// <exception cref="System.ObjectDisposedException">Thrown when </exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown when </exception>
        /// <exception cref="System.ArgumentException">Thrown when </exception>
        /// <exception cref="System.FormatException">Thrown when </exception>
        public static string EncodePassword(string originalPassword)
        {
            //// convert to Bytes
            Encoder enc = System.Text.Encoding.Unicode.GetEncoder();

            byte[] unicodeText = new byte[originalPassword.Length * 2];

            enc.GetBytes(originalPassword.ToCharArray(), 0, originalPassword.Length, unicodeText, 0, true);

            MD5 md5 = new MD5CryptoServiceProvider();

            byte[] result = md5.ComputeHash(unicodeText);
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < result.Length; i++)
            {
                sb.Append(result[i].ToString("X2"));
            }

            return sb.ToString();
        }

        /// <summary>
        /// Validates an input value
        /// </summary>
        /// <param name="inputValue">Specified input value</param>
        /// <returns>True | Falue - Value is valid</returns>
        private static bool ValidateInput(string inputValue)
        {

            bool valid = !string.IsNullOrEmpty(inputValue);
            if (!valid)
            {
                throw new EncryptionException(EncryptionException.Code.InvalidInputValues, MethodBase.GetCurrentMethod());
            }

            return valid;
        }

    }
}